package com.scrm.server.wx.cp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scrm.api.wx.cp.dto.CustomerDynamicInfoDTO;
import com.scrm.api.wx.cp.dto.FissionPosterParamDTO;
import com.scrm.api.wx.cp.entity.*;
import com.scrm.api.wx.cp.enums.BrCustomerDynamicModelEnum;
import com.scrm.api.wx.cp.enums.BrCustomerDynamicTypeEnum;
import com.scrm.api.wx.cp.vo.WxCustomerVO;
import com.scrm.api.wx.cp.vo.WxFissionContactQueryVO;
import com.scrm.api.wx.cp.vo.WxFissionContactVO;
import com.scrm.common.constant.Constants;
import com.scrm.common.exception.BaseException;
import com.scrm.common.util.FileUtils;
import com.scrm.common.util.ImgUtils;
import com.scrm.common.util.ListUtils;
import com.scrm.common.util.UUID;
import com.scrm.server.wx.cp.mapper.WxFissionContactMapper;
import com.scrm.server.wx.cp.service.*;
import com.scrm.server.wx.cp.utils.COSUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.bean.external.WxCpContactWayResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 企微应用宝-渠道码 服务实现类
 * @author xxh
 * @since 2022-03-21
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class WxFissionContactServiceImpl extends ServiceImpl<WxFissionContactMapper, WxFissionContact> implements IWxFissionContactService {

    @Autowired
    private IWxFissionTaskService wxFissionTaskService;

    @Autowired
    private IContactWayService contactWayService;

    @Autowired
    private IWxFissionCustomerService wxFissionCustomerService;

    @Autowired
    private IWxTempFileService wxTempFileService;

    @Autowired
    private IWxCustomerService customerService;

    @Autowired
    private COSUtils cosUtils;

    @Autowired
    private IWxFissionTaskCustomerDetailService taskCustomerDetailService;

    @Autowired
    private IWxCustomerStaffService customerStaffService;

    @Autowired
    private IStaffService staffService;

    @Autowired
    private IWxContactExpirationService contactExpirationService;

    @Autowired
    private IBrCustomerDynamicService customerDynamicService;

    @Override
    public WxFissionContact checkExists(String id){
        if (StringUtils.isBlank(id)) {
            return null;
        }
        WxFissionContact byId = getById(id);
        if (byId == null) {
            throw new BaseException("企微应用宝-渠道码不存在");
        }
        return byId;
    }

    @Override
    public String getPoster(FissionPosterParamDTO dto) {

        //先查任务信息
        WxFissionTask wxFissionTask = wxFissionTaskService.checkExists(dto.getTaskId());

        //再查客户信息
        WxCustomer wxCustomer = customerService.checkExists(dto.getCorpId(), dto.getExtCustomerId());

        //条件查询
        LambdaQueryWrapper<WxFissionContact> query = new QueryWrapper<WxFissionContact>().lambda()
                .eq(WxFissionContact::getExtCorpId, dto.getCorpId())
                .eq(WxFissionContact::getTaskId, dto.getTaskId())
                .eq(WxFissionContact::getExtCustomerId, dto.getExtCustomerId());

        WxFissionContact fissionContact = getOne(query, false);

        File poster = null;
        try{
            //首次生成，用户首次参与活动
            if (fissionContact == null || fissionContact.getId() == null) {
                //先生成它的渠道码
                fissionContact = createContact(wxFissionTask, dto);
                //生成海报
                poster = buildPoster(wxFissionTask, fissionContact, wxCustomer);
                //传到cos
                WxTempFile wxTempFile = wxTempFileService.uploadToCos(poster, "企微应用宝用户海报", dto.getCorpId());
                //数据入库
                fissionContact.setPosterFileId(wxTempFile.getId());
                save(fissionContact);
                //首次生成活动直接生成客户活动详情
                taskCustomerDetailService.initDetails(wxFissionTask, dto.getExtCustomerId());
                //首次生成活动海报记录客户动态
                CustomerDynamicInfoDTO dynamicInfoDTO = new CustomerDynamicInfoDTO()
                        .setTaskId(wxFissionTask.getId()).setTaskName(wxFissionTask.getName());
                customerDynamicService.save(BrCustomerDynamicModelEnum.TASK_TREASURE.getCode(),
                        BrCustomerDynamicTypeEnum.FISSION_FRIENDS.getCode(), dto.getCorpId(), null,
                        dto.getExtCustomerId(), dynamicInfoDTO);
            }else if(!Objects.equals(wxFissionTask.getPosterFileId(), fissionContact.getTheLastPosterFileId())){
                //任务改了其他海报，这时需要重新生成海报
                //删旧海报
                wxTempFileService.deleteByIds(Collections.singletonList(fissionContact.getPosterFileId()));
                //生成新海报
                poster = buildPoster(wxFissionTask, fissionContact, wxCustomer);
                //传到cos
                WxTempFile wxTempFile = wxTempFileService.uploadToCos(poster, "企微应用宝用户海报", dto.getCorpId());
                //数据入库
                fissionContact.setPosterFileId(wxTempFile.getId());
                updateById(fissionContact);
            }
            //直接从cos下载返回（最后都走这一步返回，上面是更新数据库和传到cos）
            WxTempFile tempFile = wxTempFileService.getById(fissionContact.getPosterFileId());
            return cosUtils.getObjectUrl(tempFile.getFilePath());
        }catch (RuntimeException | IOException e){
            log.error("获取海报异常，", e);
            throw new BaseException("获取海报失败！");
        }  finally {
            if (poster != null && poster.exists()) {
                poster.deleteOnExit();
            }
        }

    }

    @Override
    public IPage<WxFissionContactVO> findCountList(WxFissionContactQueryVO queryVO) {

        LambdaQueryWrapper<WxFissionContact> queryWrapper = buildQuery(queryVO);

        if (queryWrapper == null) {
            return new Page<>();
        }

        Page<WxFissionContact> page = page(new Page<>(queryVO.getPageNum(), queryVO.getPageSize()), queryWrapper);
        return page.convert(this::translation);
    }

    @Override
    public void saveSystem(WxFissionTask fissionTask) {

        String id = UUID.get16UUID();

        String state = Constants.FISSION_CONTACT_WAY_STATE_PRE + id + "-s";

        //生成渠道活码
        WxCpContactWayResult contact = contactWayService.getCommonContact(fissionTask.getExtCorpId(), null,
                state , fissionTask.getExtStaffIds(), true);

        //设置二维码过期时间
        contactExpirationService.saveOrUpdate(fissionTask.getExtCorpId(), contact.getConfigId(), fissionTask.getId(), fissionTask.getCodeExpiredTime());

        WxFissionContact wxFissionContact = new WxFissionContact().setId(id)
                .setExtCorpId(fissionTask.getExtCorpId())
                .setTaskId(fissionTask.getId())
                .setHasSystem(true)
                .setConfigId(contact.getConfigId())
                .setState(state)
                .setQrCode(contact.getQrCode())
                .setHasNewCustomer(false)
                .setCreatedAt(new Date())
                .setUpdatedAt(new Date());

        save(wxFissionContact);
    }

    @Override
    public void updateStaffIds(String extCorpId, String taskId, List<String> extStaffIds) {

        List<WxFissionContact> contactList = list(new QueryWrapper<WxFissionContact>().lambda()
                .eq(WxFissionContact::getExtCorpId, extCorpId)
                .eq(WxFissionContact::getTaskId, taskId));

        contactList.forEach(e ->
            contactWayService.getCommonContact(extCorpId, e.getConfigId(), e.getState(), extStaffIds, true)
        );
    }

    @Override
    public WxFissionContact getSysContact(String extCorpId, String taskId) {

        WxFissionContact contact = getOne(new QueryWrapper<WxFissionContact>().lambda()
                .eq(WxFissionContact::getExtCorpId, extCorpId)
                .eq(WxFissionContact::getTaskId, taskId)
                .eq(WxFissionContact::getHasSystem, true), false);

        if (contact == null || contact.getId() == null) {
            throw new BaseException("找不到该任务的渠道活码");
        }

        return contact;
    }

    /**
     * 翻译
     * @param wxFissionContact
     * @return
     */
    private WxFissionContactVO translation(WxFissionContact wxFissionContact) {
        WxFissionContactVO resVO = new WxFissionContactVO();
        BeanUtils.copyProperties(wxFissionContact, resVO);

        //客户信息
        resVO.setCustomer(customerService.find(wxFissionContact.getExtCorpId(), wxFissionContact.getExtCustomerId()));

        //所属员工
        List<WxCustomerStaff> staffList = customerStaffService.list(new QueryWrapper<WxCustomerStaff>().lambda()
                .eq(WxCustomerStaff::getExtCorpId, resVO.getExtCorpId())
                .eq(WxCustomerStaff::getExtCustomerId, resVO.getExtCustomerId()));
        if (ListUtils.isNotEmpty(staffList)) {
            List<String> staffExtIds = staffList.stream().map(WxCustomerStaff::getExtStaffId).collect(Collectors.toList());
            resVO.setStaffList(staffService.listByExtIds(resVO.getExtCorpId(), staffExtIds));
        }

        //邀请来源
        List<WxFissionCustomer> customerList = wxFissionCustomerService.list(new QueryWrapper<WxFissionCustomer>().lambda()
                .eq(WxFissionCustomer::getExtCorpId, resVO.getExtCorpId())
                .eq(WxFissionCustomer::getTaskId, resVO.getTaskId())
                .eq(WxFissionCustomer::getExtCustomerId, resVO.getExtCustomerId())
                .orderByDesc(WxFissionCustomer::getCreatedAt));

        if (ListUtils.isNotEmpty(customerList)) {
            resVO.setOrigin(customerService.find(resVO.getExtCorpId(), customerList.get(0).getExtInviteCustomerId()));
        }

        //已完成任务阶段
        List<WxFissionTaskCustomerDetail> details = taskCustomerDetailService.list(new QueryWrapper<WxFissionTaskCustomerDetail>().lambda()
                .eq(WxFissionTaskCustomerDetail::getExtCorpId, resVO.getExtCorpId())
                .eq(WxFissionTaskCustomerDetail::getTaskId, resVO.getTaskId())
                .eq(WxFissionTaskCustomerDetail::getExtCustomerId, resVO.getExtCustomerId())
                .orderByAsc(WxFissionTaskCustomerDetail::getStage));

        if (ListUtils.isNotEmpty(details)) {
            //完成状态
            resVO.setHasFinish(details.get(details.size() - 1).getHasFinish());
            //完成阶段
            for (int i = details.size() - 1; i >= 0; i--) {
                WxFissionTaskCustomerDetail detail = details.get(i);
                if (detail.getHasFinish()) {
                    resVO.setFinishStage(detail.getStage());
                    break;
                }
            }
            
            //完成阶段详情
            resVO.setFinishDetails(
                    details.stream().map(taskCustomerDetailService::translate).collect(Collectors.toList())
            );
        }

        //是否已流失
        resVO.setHasLose(Optional.ofNullable(resVO.getCustomer())
                .orElse(new WxCustomer()).getHasDelete() == null);

        //邀请详情
        List<WxFissionCustomer> inviteCustomerList = wxFissionCustomerService.list(new QueryWrapper<WxFissionCustomer>().lambda()
                .eq(WxFissionCustomer::getExtCorpId, resVO.getExtCorpId())
                .eq(WxFissionCustomer::getTaskId, resVO.getTaskId())
                .eq(WxFissionCustomer::getExtInviteCustomerId, resVO.getExtCustomerId()));

        resVO.setInviteNum(inviteCustomerList.size());
        resVO.setSuccessNum(
                (int) inviteCustomerList.stream()
                        .filter(e -> e.getHasFirst() && !e.getHasDeleted())
                        .count()
        );
        return resVO;
    }

    /**
     * 构造查询条件
     * @param queryVO
     * @return
     */
    private LambdaQueryWrapper<WxFissionContact> buildQuery(WxFissionContactQueryVO queryVO) {

        //完成阶段
        List<String> finishStageCustomerIds = null;
        //如果有阶段这个查询条件
        if (queryVO.getFinishStage() != null) {
            List<WxFissionTaskCustomerDetail> details = taskCustomerDetailService.list(new QueryWrapper<WxFissionTaskCustomerDetail>().lambda()
                    .eq(WxFissionTaskCustomerDetail::getExtCorpId, queryVO.getExtCorpId())
                    .eq(WxFissionTaskCustomerDetail::getTaskId, queryVO.getId())
                    .eq(WxFissionTaskCustomerDetail::getStage, queryVO.getFinishStage())
                    .eq(WxFissionTaskCustomerDetail::getHasFinish, true));

            if (ListUtils.isEmpty(details)) {
                return null;
            }
            finishStageCustomerIds = details.stream().map(WxFissionTaskCustomerDetail::getExtCustomerId).collect(Collectors.toList());
        }

        //客户昵称
        List<String> nameExtIds = null;
        if (StringUtils.isNotBlank(queryVO.getName())) {
            nameExtIds = customerService.findByNameIncludeDel(queryVO.getExtCorpId(), queryVO.getName());
            if (ListUtils.isEmpty(nameExtIds)) {
                return null;
            }
        }

        //流失状态,内存处理
        List<String> loseExtIds = null;
        if (queryVO.getHasLose() != null) {
            //先查出这个任务的全部参与客户id
            List<WxFissionContact> contactList = list(new QueryWrapper<WxFissionContact>().lambda()
                    .eq(WxFissionContact::getExtCorpId, queryVO.getExtCorpId())
                    .eq(WxFissionContact::getTaskId, queryVO.getId())
                    .eq(WxFissionContact::getHasSystem, false)
                    .in(ListUtils.isNotEmpty(finishStageCustomerIds), WxFissionContact::getExtCustomerId, finishStageCustomerIds)
                    .in(ListUtils.isNotEmpty(nameExtIds), WxFissionContact::getExtCustomerId, nameExtIds));

            if (ListUtils.isEmpty(contactList)) {
                return null;
            }

            //extIds
            List<String> extIds = contactList.stream()
                    .map(WxFissionContact::getExtCustomerId)
                    .collect(Collectors.toList());

            List<WxCustomerVO> customerList = customerService.findByExtIdsIncludeDel(queryVO.getExtCorpId(), extIds);

            //未流失的
            List<String> noLoseCustomerExtIds = customerList.stream()
                    .filter(e -> e.getHasDelete() != null)
                    .map(WxCustomerVO::getExtId).collect(Collectors.toList());

            //流失
            List<String> loseCustomerExtIds = new ArrayList<>();
            //看流失状态
            for (WxCustomerVO wxCustomerVO : customerList) {
                if (wxCustomerVO.getHasDelete() == null
                        && !noLoseCustomerExtIds.contains(wxCustomerVO.getExtId())) {
                    loseCustomerExtIds.add(wxCustomerVO.getExtId());
                }
            }
            loseExtIds = queryVO.getHasLose() ? loseCustomerExtIds: noLoseCustomerExtIds;
            if (ListUtils.isEmpty(loseExtIds)) {
                return null;
            }
        }
        return new QueryWrapper<WxFissionContact>().lambda()
                .eq(WxFissionContact::getExtCorpId, queryVO.getExtCorpId())
                .eq(WxFissionContact::getTaskId, queryVO.getId())
                .eq(WxFissionContact::getHasSystem, false)
                .in(ListUtils.isNotEmpty(finishStageCustomerIds), WxFissionContact::getExtCustomerId, finishStageCustomerIds)
                .in(ListUtils.isNotEmpty(nameExtIds), WxFissionContact::getExtCustomerId, nameExtIds)
                .in(ListUtils.isNotEmpty(loseExtIds), WxFissionContact::getExtCustomerId, loseExtIds)
                .orderByDesc(WxFissionContact::getCreatedAt);
    }

    private WxFissionContact createContact(WxFissionTask wxFissionTask, FissionPosterParamDTO dto) {

        String id = UUID.get16UUID();

        //获取渠道活码
//        String state = getStateByTaskId(dto.getExtCorpId(), dto.getTaskId());
        String state = Constants.FISSION_CONTACT_WAY_STATE_PRE + id;

        WxCpContactWayResult contact = contactWayService.getCommonContact(wxFissionTask.getExtCorpId(), null,
                state, wxFissionTask.getExtStaffIds(), true);

        //过期时间
        contactExpirationService.saveOrUpdate(wxFissionTask.getExtCorpId(), contact.getConfigId(), wxFissionTask.getId(), wxFissionTask.getCodeExpiredTime());

        //获取是否是裂变来的客户
        boolean hasNewCustomer = wxFissionCustomerService.count(new QueryWrapper<WxFissionCustomer>().lambda()
                .eq(WxFissionCustomer::getExtCorpId, dto.getCorpId())
                .eq(WxFissionCustomer::getTaskId, dto.getTaskId())
                .eq(WxFissionCustomer::getExtCustomerId, dto.getExtCustomerId())) > 0;

        return new WxFissionContact().setId(id).setExtCorpId(dto.getCorpId())
                .setConfigId(contact.getConfigId()).setQrCode(contact.getQrCode()).setState(state)
                .setExtCustomerId(dto.getExtCustomerId()).setHasNewCustomer(hasNewCustomer)
                .setTaskId(dto.getTaskId()).setCreatedAt(new Date()).setUpdatedAt(new Date())
                .setTheLastPosterFileId(wxFissionTask.getPosterFileId()).setHasSystem(false);
    }

    /**
     * 生成客户专属海报
     * @param wxFissionTask
     * @param wxFissionContact
     * @return
     */
    private File buildPoster(WxFissionTask wxFissionTask, WxFissionContact wxFissionContact, WxCustomer wxCustomer) throws FileNotFoundException {
        //临时文件夹
        String tempFolderPath = System.getProperty("user.dir") + "/tmpPath/" + wxFissionContact.getId() + "/";

        //临时文件
        File posterFile = FileUtils.createFile(tempFolderPath + "poster.png");
        File headFile = FileUtils.createFile(tempFolderPath + "head.png");
        File headResizeFile = FileUtils.createFile(tempFolderPath + "headResize.png");
        File contactFile = FileUtils.createFile(tempFolderPath + "contact.png");
        File contactResizeFile = FileUtils.createFile(tempFolderPath + "contactResize.png");
        File tempFile = FileUtils.createFile(tempFolderPath + "temp.png");
        //目标文件
        File resFile = FileUtils.createFile(tempFolderPath + "res.png");
        //先调整海报尺寸
        WxTempFile posterTempFile = wxTempFileService.checkExists(wxFissionTask.getPosterFileId());
        try {
            cosUtils.download(posterFile, posterTempFile.getFilePath());
            //调整海报大小
            new ImgUtils().reSize(posterFile, resFile, wxFissionTask.getPosterSize().getWidth(), wxFissionTask.getPosterSize().getHeight());
            //下载渠道活码
            wxTempFileService.downloadFile(wxFissionContact.getQrCode(), contactFile);
            //调整渠道活码尺寸
            new ImgUtils().reSize(contactFile, contactResizeFile, wxFissionTask.getQrCodePos().getWidth(), wxFissionTask.getQrCodePos().getHeight());
            //拷贝到临时文件
            FileUtils.copyFile(resFile, tempFile);
            //把渠道活码写进海报里
            new ImgUtils().overlyingImage(tempFile, contactResizeFile, resFile, wxFissionTask.getQrCodePos().getX(), wxFissionTask.getQrCodePos().getY());
            //用户名
            if (wxFissionTask.getHasName()) {
                Color color = new Color(wxFissionTask.getNameColor().getRed(), wxFissionTask.getNameColor().getGreen(), wxFissionTask.getNameColor().getBlue());
                FileUtils.copyFile(resFile, tempFile);
                new ImgUtils().addFont(tempFile, resFile, wxFissionTask.getNamePos().getX(), wxFissionTask.getNamePos().getY(), wxCustomer.getName(), color);
            }
            //用户头像
            if (wxFissionTask.getHasHead() && StringUtils.isNotBlank(wxCustomer.getAvatar())) {
                wxTempFileService.downloadFile(wxCustomer.getAvatar(), headFile);
                //调整大小
                new ImgUtils().reSize(headFile, headResizeFile, wxFissionTask.getHeadPos().getWidth(), wxFissionTask.getHeadPos().getHeight());
                //复制一个临时文件
                FileUtils.copyFile(resFile, tempFile);
                //写进去
                new ImgUtils().overlyingImage(tempFile, headResizeFile, resFile, wxFissionTask.getHeadPos().getX(), wxFissionTask.getHeadPos().getY());
            }
        } finally {
            posterFile.deleteOnExit();
            headFile.deleteOnExit();
            contactFile.deleteOnExit();
            tempFile.deleteOnExit();
        }
        return resFile;
    }

}
